#ifndef LAYOUT_HPP
#define LAYOUT_HPP

#include "layout.h"

template <typename Range>
int2 select_corner(const Range& rng, int2 direction) noexcept
{
	// center + direction * half_size
	return
	(
		(lower(rng) + upper(rng)) // center * 2
		+ direction * (upper(rng) - lower(rng)) // direction * size
	) / 2; // all halved
}

// support::deref here will deref a thing as much as possible, or not at all if not possible
// this way we support any level of indirection in our range of objects that we can layout
// now this does adjacent_difference, and I would love to use std, but many caveats...
// first std::adjacent_difference very liberal with assuming value types, makes a temp stores intermediate values,
// can't necessarily do that with move only heavy objects which is the whole reason why we can get indirection here,
// but ok then deal with the indirection, the "pointers" themselves are value types and you can throw those around just fine...
// yeah but the pointers are const... cause I don't want you changing the pointers on me I want you to just modify the pointees
// so yeah, adjacent difference just can't work here...
// support::variance could work with magic iterator that would do support::deref under the hood, but I can't be bothered anymore,
// will do that if I find one more use case for it, otherwise better to just try to steer clear of heavy objects and indirection
template <typename ForwardItr>
constexpr void layout_bounds(ForwardItr begin, ForwardItr end, const int2& spacing)
{
	assert(begin != end);
	const auto mask = int2(spacing != int2::zero());
	const auto corner = signum(spacing);

	for(auto prev = begin++; begin != end; ++begin, ++prev)
	{
		support::deref(begin) +=
		(
			select_corner(support::deref(prev), corner)
			+ spacing
			- select_corner(support::deref(begin), -corner)
		) * mask;
	}
}

template <typename Range>
bounds_layout<Range>::bounds_layout(Range elements, int2 spacing) :
	movable_bounds(invalid_range),
	elements(std::move(elements)),
	spacing(spacing)
{ }

template <typename Range>
bounds_layout<Range>::bounds_layout(int2 spacing) :
	movable_bounds(invalid_range),
	elements{},
	spacing(spacing)
{ }

template <typename Range>
auto bounds_layout<Range>::operator+=(const int2& offset)
	-> bounds_layout&
{
	movable_bounds::operator+=(offset);
	for(auto&& element : elements)
		support::deref(element) += offset;
	return *this;
}

template <typename Range>
range2D bounds_layout<Range>::update()
{
	using std::begin;
	using std::end;
	layout_bounds(begin(elements), end(elements), spacing);

	using ::lower;
	using ::upper;
	bounds = invalid_range; // invalid range, returned for empty layout
	for(auto&& element : elements)
	{
		bounds.lower() = min(bounds.lower(), lower(support::deref(element)));
		bounds.upper() = max(bounds.upper(), upper(support::deref(element)));
	}
	return bounds;
}

#endif /* end of include guard */
